from collections import namedtuple
from copy import deepcopy
import itertools
import random

from libtcodpy import Color

import backend
from ai import DefaultAI


Item = namedtuple('Item', ['slots', 'quantity', 'stats', 'skills', 'misc_stats', 'abilities'])
item = Item(['RH'], 1, {}, {}, {}, set())

def test_dict_values():
    default_dict = {'abilities': set(), 'color': Color(40, 40, 40), 'is_player': False, 'scene': None, 'move_stats': {},
                    'skill_ranks': {'archery': 0, 'force': 0, 'evasion': 0, 'smithing': 0, 'stealth': 0, 'detection': 0,
                                    'guard': 0, 'lore': 0, 'accuracy': 0},
                    'stats': {'stamina': 1, 'strength': 1, 'focus': 1},
                    'bonus_skills': {'archery': 0, 'force': 0, 'evasion': 0, 'smithing': 0, 'stealth': 0, 'detection': 0,
                                     'guard': 0, 'lore': 0, 'accuracy': 0}, 'destination': None, 'character': '@',
                    'equipment': {'RH': None}, 'location': (40, 25), 'WAIT': 1,
                    'misc_stats': {'focus_heal_time': 20, 'focus_heal_count': 0, 'strength_heal_time': 100,
                                   'strength_heal_count': 0, 'stamina_heal_time': 50, 'attack_time': 10, 'move_time': 10,
                                   'resistance': 0, 'daze': 0, 'stamina_heal_count': 0, 'lethality': 1, 'clarity': 0,
                                   'stun': 0},
                    'inventory': [], 'carrying_capacity': 100, 'burden': 0, 'path': None,
                    'base_stats': {'stamina': 1, 'strength': 1, 'focus': 1},
                    'target': None, 'name': 'default', 'path_index': 0,
                    'skills': {'archery': 1, 'force': 1, 'evasion': 1, 'smithing': 1, 'stealth': 1, 'detection': 1,
                               'guard': 1, 'lore': 1, 'accuracy': 1}, 'ai': DefaultAI(), 'exp': 0, 'alive': True}
    u = backend.Unit(name='default')
    u.WAIT = 1
    for key in u.__dict__.keys():
        yield check_default_unit, u.__dict__[key], default_dict[key]

def check_default_unit(u_value, d_value):
    assert u_value == d_value

def test_default_unit_WAIT():
    waits = [backend.Unit('').WAIT for _ in xrange(1000)]
    assert max(waits) == 10
    assert min(waits) == 1

def test_equip():
    u = backend.Unit()
    u.inventory = [item]
    u.equip(item)
    assert u.equipment == {'RH': item}

def test_equip_with_stats():
    for stats in itertools.product(xrange(-2, 3), repeat=3):
        yield check_equip_with_stats, dict(zip(['strength', 'stamina', 'focus'], stats))

def check_equip_with_stats(stats):
    unit = backend.Unit(stats={'strength': 1, 'stamina': 1, 'focus': 1})
    stat_stick = Item(['RH'], 1, stats, {}, {}, set())
    unit.inventory = [stat_stick]
    unit.equip(stat_stick)
    if stats['strength'] < 0:
        assert unit.alive == False
    for stat in stats:
        if stats[stat] < 0:
            assert unit.stats[stat] == 0
        else:
            assert unit.stats[stat] == stats[stat] + 1

def test_equip_with_skills():
    for skills in random.sample(list(itertools.product(xrange(-2, 3), repeat=8)), 25):
        yield check_equip_with_skills, dict(zip(['force', 'guard', 'smithing', 'accuracy', 'evasion', 'archery',
                                                 'stealth', 'detection', 'lore'], skills))

def check_equip_with_skills(skills):
    unit = backend.Unit()
    unit.skills = {'force': 1, 'guard': 1, 'smithing': 1, 'accuracy': 1, 'evasion': 1, 'archery': 1, 'stealth': 1,
                   'detection': 1, 'lore': 1}
    skill_stick = Item(['RH'], 1, {}, skills, {}, set())
    unit.inventory = [skill_stick]
    unit.equip(skill_stick)
    for skill in skills:
        assert unit.skills[skill] == skills[skill] + 1

def test_unequip():
    unit = backend.Unit()
    unit.equipment['RH'] = item
    unit.unequip(item)
    assert unit.inventory == [item]
    assert unit.equipment['RH'] is None

def test_gain_ability():
    u = backend.Unit()
    ability = 'ability'
    u.gain_ability(ability)
    assert u.abilities == {'ability'}

def test_create_with_item():
    u = backend.Unit(inventory=[item])
    assert u.inventory == [item]

def test_create_with_ability():
    abilities = {'ability'}
    u = backend.Unit(abilities=abilities)
    assert u.abilities == abilities

def test_alter_stats():
    for stat in ['strength', 'stamina', 'focus']:
        for amount in xrange(-10, 11):
            yield check_alter_stats, stat, amount

def check_alter_stats(stat, amount):
    unit = backend.Unit(stats={'strength': 1, 'stamina': 1, 'focus': 1})
    died = unit.alter_stats({stat: amount})
    if amount < 0:
        if stat != 'strength':
            assert died is None
        assert unit.stats[stat] == 0
    else:
        assert died is None
        assert unit.stats[stat] == 1
        unit.base_stats[stat] += amount + 1
        unit.alter_stats({stat: amount})
        assert unit.stats[stat] == amount + 1


def test_death():
    for i in [-1, -10]:
        yield check_death, i

def check_death(str_dmg):
    u = backend.Unit(stats={'strength': 1, 'stamina': 1, 'focus': 1})
    assert u.alter_stats({'strength': str_dmg}) == 'die'
    assert u.stats['strength'] == 0